package resolver

/*
Copyright 2021-2025 The k8gb Contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic
*/

import (
	"fmt"
	"net"
	"reflect"
	"unicode"

	"github.com/go-playground/validator/v10"
)

func onlyLettersDigitsDash(fl validator.FieldLevel) bool {
	for _, r := range fl.Field().String() {
		if !(unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-') {
			return false
		}
	}
	return true
}

func uniqueStrings(fl validator.FieldLevel) bool {
	values := fl.Field()
	if values.Kind() != reflect.Slice {
		return false
	}
	seen := make(map[string]bool)
	for i := 0; i < values.Len(); i++ {
		val := values.Index(i).String()
		if _, ok := seen[val]; ok {
			return false
		}
		seen[val] = true
	}
	return true
}

func isIPOrHostnameOrHostPort(fl validator.FieldLevel) bool {
	v := validator.New()
	val := fl.Field().String()
	// Valid hostname:port or ip:port
	if err := v.Var(val, "hostname_port"); err == nil {
		host, _, _ := net.SplitHostPort(val)
		if host == "" {
			return false // forbid empty host, e.g. ":53"
		}
		return true
	}
	// Valid hostname (RFC1123)
	if err := v.Var(val, "hostname_rfc1123"); err == nil {
		return true
	}
	// Valid IPv4 address
	if err := v.Var(val, "ipv4"); err == nil {
		return true
	}
	return false
}

// cross field specValidator
func parentZoneOrFallbackRequired(sl validator.StructLevel) {
	cfg := sl.Current().Interface().(Config)

	// If ParentZoneDNSServersRaw contains at least one item, validation passes.
	if len(cfg.ParentZoneDNSServersRaw) > 0 {
		return
	}
	// Otherwise, both FallbackEdgeDNSServerNameRaw and FallbackEdgeDNSServerPortRaw must be set.
	if cfg.FallbackEdgeDNSServerNameRaw != "" && cfg.FallbackEdgeDNSServerPortRaw > 0 {
		return
	}
	// If neither condition is met, report a struct-level validation error.
	sl.ReportError(cfg.ParentZoneDNSServersRaw, "ParentZoneDNSServersRaw", "ParentZoneDNSServersRaw", "parentorfallback", "")
}

func noMultipleProviders(sl validator.StructLevel) {
	cfg := sl.Current().Interface().(Config)
	dnsType, types := cfg.getEdgeDNSType()
	if dnsType == DNSTypeMultipleProviders {
		sl.ReportError(cfg.ExtDNSEnabledRaw, "ExtDNSEnabledRaw", "ExtDNSEnabledRaw", "multipleproviders",
			fmt.Sprintf("you have multiple DNS providers configured simultaneously %s", types))
	}
}

func ValidateInfobloxStruct(sl validator.StructLevel) {
	cfg := sl.Current().Interface().(Config)

	// If Host is empty, skip
	if cfg.Infoblox.Host == "" {
		return
	}
	v := validator.New()

	// Validate Host: must be valid hostname or IP address
	if err := v.Var(cfg.Infoblox.Host, "hostname|ip"); err != nil {
		sl.ReportError(cfg.Infoblox.Host, "Host", "Host", "hostnameOrIP", "")
	}

	// Validate Version: must not be empty
	if err := v.Var(cfg.Infoblox.Version, "required"); err != nil {
		sl.ReportError(cfg.Infoblox.Version, "Version", "Version", "required", "")
	}

	// Validate Port: must be greater than 0 and less than or equal to 65535
	if err := v.Var(cfg.Infoblox.Port, "gt=0,lte=65535"); err != nil {
		sl.ReportError(cfg.Infoblox.Port, "Port", "Port", "portRange", "")
	}

	// Validate Username: must not be empty
	if err := v.Var(cfg.Infoblox.Username, "required"); err != nil {
		sl.ReportError(cfg.Infoblox.Username, "Username", "Username", "required", "")
	}

	// Validate Password: must not be empty
	if err := v.Var(cfg.Infoblox.Password, "required"); err != nil {
		sl.ReportError(cfg.Infoblox.Password, "Password", "Password", "required", "")
	}

	// Validate HTTPPoolConnections: must be greater than or equal to 0
	if err := v.Var(cfg.Infoblox.HTTPPoolConnections, "gte=0"); err != nil {
		sl.ReportError(cfg.Infoblox.HTTPPoolConnections, "HTTPPoolConnections", "HTTPPoolConnections", "min", "")
	}

	// Validate HTTPRequestTimeout: must be greater than 0
	if err := v.Var(cfg.Infoblox.HTTPRequestTimeout, "gt=0"); err != nil {
		sl.ReportError(cfg.Infoblox.HTTPRequestTimeout, "HTTPRequestTimeout", "HTTPRequestTimeout", "min", "")
	}
}
